#include "mongoose.h"
#include "cJSON.h"
#include "device_user.h"
#include "recorder.h"
#include <stdlib.h>
#include "comm_api.h"

#define cJSON_free free




#if 1
int device_userprintf (const char *format, ...){(void)format;return 0;}
#define  printf       device_userprintf
#endif

typedef struct StDevMgr {
    unsigned char sn[50];
    char host[100];
    char key[100];
    char token_tmp[100];
}StDevMgr;

struct StDevMgr gDevMgr;

typedef struct StWorkData {
    int status;
    char *new_data;
    int new_len;
}StWorkData;

#if 0
void hex_dump(const char *desc, const void *addr, const int len) {
    int i;
    unsigned char buff[17];
    unsigned char *pc = (unsigned char*)addr;

    // Output description if given.
    if (desc != NULL)
        printf("%s:\r\n", desc);

    // Process every byte in the data.
    for (i = 0; i < len; i++) {
        // Multiple of 16 means new line (with line offset).

        if ((i % 16) == 0) {
            // Just don't print ASCII for the zeroth line.
            if (i != 0)
                printf("  %s\r\n", buff);

            // Output the offset.
            printf("  %04x ", i);
        }

        // Now the hex code for the specific character.
        printf(" %02x", pc[i]);

        // And store a printable ASCII character for later.
        if ((pc[i] < 0x20) || (pc[i] > 0x7e))
            buff[i % 16] = '.';
        else
            buff[i % 16] = pc[i];
        buff[(i % 16) + 1] = '\0';
    }

    // Pad out last line if not exactly 16 characters.
    while ((i % 16) != 0) {
        printf("   ");
        i++;
    }

    // And print the final ASCII bit.
    printf("  %s\r\n", buff);
}
#endif

int DevMgrInit()
{
    struct StDevMgr *pDevMgr = &gDevMgr;
    memset(pDevMgr, 0, sizeof(struct StDevMgr));

    Get_DeviceSn((char*)pDevMgr->sn,sizeof(pDevMgr->sn));
//    memcpy(pDevMgr->sn, "DJYOS0001", strlen("DJYOS0001"));
    //memcpy(pDevMgr->host, "wangxi.cpolar.cn", strlen("wangxi.cpolar.cn"));
    //memcpy(pDevMgr->host, "112.124.209.14", strlen("112.124.209.14"));
//    strcpy(pDevMgr->host, "kouyu.cpolar.cn");
    strcpy(pDevMgr->host, "kytapi.englishcaptain.com");
    strcpy(pDevMgr->key, "123");
    //memcpy(pDevMgr->key, "123", strlen("123"));
    return 0;
}

int DoHttpBody(char *body, int len, char *outbuf, int outlen)
{
    int ret = 0;
    int flag = 0;
    cJSON *cjson = 0;
    char *pJsonStr = 0;
    if (body == 0 || len <= 0) return -1;

    cjson = cJSON_Parse(body);
    if (cjson)
    {
        cJSON*  results = cJSON_GetObjectItem(cjson, "success");
        if (results)
        {
            //printf("submit success: %d\r\n", results->valueint);
            flag = results->valueint;
        }
        results = cJSON_GetObjectItem(cjson, "data");
        if (flag && results)
        {
            pJsonStr = cJSON_PrintUnformatted(results);
            //printf("json_str: %s!\r\n\r\n", pJsonStr);
            ret = strlen(pJsonStr) + 1;
            if (outbuf==0 || outlen < ret) {
                ret = -2;
                goto END_FUN;
            }
            strcpy(outbuf, pJsonStr);
        }
        results = cJSON_GetObjectItem(cjson, "msg");
        if (results)
        {
            //printf("submit message: %s\r\n", results->valuestring);
        }
    }

END_FUN:
    if (cjson) cJSON_Delete(cjson);
    if (pJsonStr) free(pJsonStr);
    return ret;
}

static void cb_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
    struct http_message *hm = (struct http_message *) ev_data;
    struct StWorkData *pQuestData = (struct StWorkData*)nc->user_data;
    char *p = 0;

    int is_chunked = 0;
    struct mg_str *s;
    if (hm) {
        if ((s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
            mg_vcasecmp(s, "chunked") == 0) {
            is_chunked = 1;
        }
    }

    switch (ev) {
    case MG_EV_CONNECT:
        if (*(int *)ev_data == -1) {
            /*  fprintf(stderr, "connect() failed: %s\n", strerror(*(int *)ev_data));*/
            nc->flags |= MG_F_CLOSE_IMMEDIATELY;
            pQuestData->status = -1;
            break;
        }
        int opt = 8 * 1024;
        if (0 != setsockopt(nc->sock, SOL_SOCKET, SO_RCVBUF, &opt, 4))
        {
            printf("Client: setsockopt failed!\n\r");
            nc->flags |= MG_F_CLOSE_IMMEDIATELY;
            pQuestData->status = -1;
        }
        if (pQuestData->new_data) {
            free(pQuestData->new_data);
        }
        pQuestData->new_data = 0;
        pQuestData->new_len = 0;
        break;
    case MG_EV_HTTP_REPLY:
    {
        switch (hm->resp_code) {
        case 200:
            nc->flags |= MG_F_CLOSE_IMMEDIATELY;

            if (hm->body.len <= 0) {
                if (pQuestData->status == 0) pQuestData->status = -1;
                break;
            }

            //process the whole body.
            pQuestData->status = -1;
            p = realloc(pQuestData->new_data, hm->body.len+1);
            if (p == 0) {
                printf("error: cb_ev_handler->realloc MG_EV_HTTP_REPLY, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            pQuestData->new_len = hm->body.len+1;
            memcpy(pQuestData->new_data, hm->body.p, hm->body.len);
            pQuestData->new_data[hm->body.len] = 0;

            if (DoHttpBody(pQuestData->new_data, pQuestData->new_len, pQuestData->new_data, pQuestData->new_len) > 0) {
                printf("%s\r\n", pQuestData->new_data);
                pQuestData->status = 1;
            }
            break;
        case 302:
            pQuestData->status = -302;
            break;
        case 401:
            pQuestData->status = -401;
            break;
        default:
            pQuestData->status = -1;
            break;
        }
        break;
    }

    case MG_EV_HTTP_CHUNK:
    {
        if (!is_chunked) break;

        nc->flags = MG_F_DELETE_CHUNK;
        //hex_dump("cb_http_upload_handler MG_EV_HTTP_CHUNK:", hm->body.p, hm->body.len);
        if (hm->body.len > 0) {
            p = realloc(pQuestData->new_data, pQuestData->new_len + hm->body.len);
            if (p == 0) {
                printf("error: cb_ev_handler->realloc MG_EV_HTTP_CHUNK, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            memcpy(&pQuestData->new_data[pQuestData->new_len], hm->body.p, hm->body.len);
            pQuestData->new_len += hm->body.len;
        }
        else if (hm->body.len == 0) {//end flag
            if (pQuestData->status != 0) break;
            p = realloc(pQuestData->new_data, pQuestData->new_len+1);
            if (p == 0) {
                printf("error: cb_ev_handler->realloc MG_EV_HTTP_CHUNK, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            pQuestData->new_data[pQuestData->new_len] = 0;
            pQuestData->new_len += 1;

            //process the whole body.
            pQuestData->status = -1;

            if (DoHttpBody(pQuestData->new_data, pQuestData->new_len, pQuestData->new_data, pQuestData->new_len) > 0) {
                printf("%s\r\n", pQuestData->new_data);
                pQuestData->status = 1;
            }
        }
        break;
    }

    case MG_EV_CLOSE:
    		if (pQuestData && pQuestData->status==0) {
    			pQuestData->status = -1;
    		}
        break;
    default:
        break;
    }
}



int GetLoginData(char *body, int len)
{
    (void)len;
    int ret = -1;
    int flag = 0;
    cJSON *cjson = 0;
    struct StDevMgr *pDevMgr = &gDevMgr;
    cjson = cJSON_Parse(body);
    if (cjson)
    {
      cJSON*  results = cJSON_GetObjectItem(cjson, "success");
      if (results)
      {
          printf("login success: %d\r\n", results->valueint);
          flag = results->valueint;
      }
      results = cJSON_GetObjectItem(cjson, "data");
      if (results)
      {
          if (flag) {
              pDevMgr->token_tmp[0] = 0;
              strcat(pDevMgr->token_tmp, (char*)pDevMgr->sn);
              strcat(pDevMgr->token_tmp, "_");
              strcat(pDevMgr->token_tmp, results->valuestring);
              ret = 1;
          }
          else {
              pDevMgr->token_tmp[0] = 0;
          }
          printf("login token: %s\r\n", results->valuestring);
      }
      results = cJSON_GetObjectItem(cjson, "msg");
      if (results)
      {
          printf("login message: %s\r\n", results->valuestring);
      }

    }
    if (cjson) cJSON_Delete(cjson);
    return ret;
}

static void cb_ev_login(struct mg_connection *nc, int ev, void *ev_data) {
    struct http_message *hm = (struct http_message *) ev_data;
    struct StWorkData *pQuestData = (struct StWorkData*)nc->user_data;
    char *p = 0;

    int is_chunked = 0;
    struct mg_str *s;
    if (hm) {
        if ((s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
            mg_vcasecmp(s, "chunked") == 0) {
            is_chunked = 1;
        }
    }

    switch (ev) {
    case MG_EV_CONNECT:
        if (*(int *)ev_data != 0) {
            /*  fprintf(stderr, "connect() failed: %s\n", strerror(*(int *)ev_data));*/
            pQuestData->status = -1;
        }
        if (pQuestData->new_data) {
            free(pQuestData->new_data);
        }
        pQuestData->new_data = 0;
        pQuestData->new_len = 0;
        break;
    case MG_EV_HTTP_REPLY:
    {
        switch (hm->resp_code) {
        case 200:
            nc->flags |= MG_F_CLOSE_IMMEDIATELY;

            if (hm->body.len <= 0) {
                if (pQuestData->status  == 0) {
                    pQuestData->status  = -1;
                }
                break;
            }

            //process the whole body.
            pQuestData->status = -1;
            p = realloc(pQuestData->new_data, hm->body.len+1);
            if (p == 0) {
                printf("error: cb_ev_login->realloc MG_EV_HTTP_REPLY, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            pQuestData->new_len = hm->body.len+1;
            memcpy(pQuestData->new_data, hm->body.p, hm->body.len);
            pQuestData->new_data[hm->body.len] = 0;

            //process the whole body.
            if (GetLoginData(pQuestData->new_data, pQuestData->new_len) > 0) {
                pQuestData->status = 1;
            }
            else {
                pQuestData->status = -1;
            }
            break;
        case 302:
            printf("http 302 not supported!\r\n");
            pQuestData->status = -302;
            break;
        case 401:
            pQuestData->status = -401;
            break;
        default:
            pQuestData->status = -1;
            break;
        }
        break;
    }

    case MG_EV_HTTP_CHUNK:
    {
        if (!is_chunked) break;

        nc->flags = MG_F_DELETE_CHUNK;
        //hex_dump("cb_http_upload_handler MG_EV_HTTP_CHUNK:", hm->body.p, hm->body.len);
        if (hm->body.len > 0) {
            p = realloc(pQuestData->new_data, pQuestData->new_len + hm->body.len);
            if (p == 0) {
                printf("error: cb_ev_login->realloc MG_EV_HTTP_CHUNK, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            memcpy(&pQuestData->new_data[pQuestData->new_len], hm->body.p, hm->body.len);
            pQuestData->new_len += hm->body.len;
        }
        else if (hm->body.len == 0) {//end flag
            if (pQuestData->status != 0) break;
            p = realloc(pQuestData->new_data, pQuestData->new_len+1);
            if (p == 0) {
                printf("error: cb_ev_login->realloc MG_EV_HTTP_CHUNK, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            pQuestData->new_data[pQuestData->new_len] = 0;
            pQuestData->new_len += 1;

            //process the whole body.
            if (GetLoginData(pQuestData->new_data, pQuestData->new_len) > 0) {
                pQuestData->status = 1;
            }
            else {
                pQuestData->status = -1;
            }
        }
        break;
    }

    case MG_EV_CLOSE:
        break;
    default:
        break;
    }
}

static int http_login(char *sn, char *sign, char *timestamp, char *boundary)
{
    struct StWorkData user_data;
    memset(&user_data, 0, sizeof(user_data));
    struct StDevMgr *pDevMgr = &gDevMgr;
    int ret = 0;
    char *meta = 0;
    char *url = 0;
    struct mg_connection *nc;
    struct mg_mgr mgr;
    mg_mgr_init(&mgr, NULL);
    const char multi_part_req_fmt[] =
        "--%s\r\n"
        "Content-Disposition: form-data; name=\"sn\"\r\n"
        "\r\n"
        "%s"
        "\r\n--%s\r\n"
        "Content-Disposition: form-data; name=\"sign\"\r\n"
        "\r\n"
        "%s"
        "\r\n--%s\r\n"
        "Content-Disposition: form-data; name=\"timestamp\"\r\n"
        "\r\n"
        "%s"
        "\r\n--%s--\r\n"
        "\r\n";

    char *multi_part_req = malloc(2*1024);
    if (multi_part_req == 0) {
        ret = -1;
        goto END_LOGIN;
    }

    snprintf(multi_part_req, 2*1024, multi_part_req_fmt, boundary, sn, boundary, sign, boundary, timestamp, boundary);
    //printf("\r\n%s\r\n", multi_part_req);

    meta = (char*)malloc(1024);
    if (meta == 0) {
        ret = -1;
        goto END_LOGIN;
    }

    url = (char*)malloc(1024);
    if (url == 0) {
        ret = -1;
        goto END_LOGIN;
    }

    user_data.new_len = 0;
    user_data.new_data = 0;

    sprintf(meta,
        "Accept: */*\r\n"
        "Connection: keep-alive\r\n"
        "User-Agent: Mongoose/6.15\r\n"
        "Cache-Control: no-cache\r\n"
        "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);

    sprintf(url, "http://%s/user/device/login", pDevMgr->host);
    nc = mg_connect_http(&mgr, cb_ev_login, url, meta, multi_part_req);
    if (nc == 0) goto END_LOGIN;
    nc->user_data = &user_data;
    unsigned int timemark = DjyGetSysTime()/1000;
    unsigned int timeout_ms = 10000;
    while (user_data.status == 0) {
        if (DjyGetSysTime()/1000-timemark > timeout_ms) {
            printf("http_login: timeout break!\r\n");
            break;
        }
        mg_mgr_poll(&mgr, 500);
        Djy_EventDelay(10*1000);
    }
    ret = user_data.status;
END_LOGIN:
    mg_mgr_free(&mgr);
    if (multi_part_req) free(multi_part_req);
    if (meta) free(meta);
    if (url) free(url);
    if (user_data.new_data) {
         free(user_data.new_data);
         user_data.new_data = 0;
         user_data.new_len = 0;
    }
    return ret;
}


//cJSON_PrintUnformatted
char *gen_json_homework(int id, struct StHWorkTopic *arr_topic, int num, char *output, int len)
{
    int min = 0;
    cJSON *root = 0;
    char *json_str = 0;
    int ret;(void)ret;
    cJSON *answers = cJSON_CreateArray();
    int i = 0;
    for (i = 0; i < num; i++) {
        struct StHWorkTopic *tmp = arr_topic + i;
        cJSON *cjson_tmp = cJSON_CreateObject();
        cJSON_AddItemToObject(cjson_tmp, "id", cJSON_CreateNumber(tmp->topic_id));
        cJSON_AddItemToObject(cjson_tmp, "answer", cJSON_CreateString(tmp->url));
        cJSON_AddItemToArray(answers, cjson_tmp);
    }

    root = cJSON_CreateObject();
    if (!root) { ret = -1; goto ERROR_RET; }
    cJSON_AddItemToObject(root, "id", cJSON_CreateNumber(id));
    cJSON_AddItemToObject(root, "answers", answers);

    json_str = cJSON_PrintUnformatted(root);
    min = strlen(json_str);
    min = min < len ? min : len;
    if (min >= len) {
        ret = -6;
        goto ERROR_RET;
    }
    memcpy(output, json_str, min);
    output[min] = 0;
    if (json_str) cJSON_free(json_str);
    //printf("=======json_str=========\r\n%s\r\n", json_str);
    cJSON_Delete(root);
    return output;

ERROR_RET:

    return 0;
}

int GetSubmitResult(char *body, int len)
{
    (void)len;
    cJSON *cjson = 0;
    int ret = -1;
    int flag = 0;
    cjson = cJSON_Parse(body);
    if (cjson)
    {
        cJSON*  results = cJSON_GetObjectItem(cjson, "success");
        if (results)
        {
            printf("submit success: %d\r\n", results->valueint);
            flag = results->valueint;
            if (flag) {
                ret = 1;
            }
        }
        results = cJSON_GetObjectItem(cjson, "data");
        if (results)
        {
            printf("data: %s\r\n", results->valuestring);
        }
        results = cJSON_GetObjectItem(cjson, "msg");
        if (results)
        {
            printf("submit message: %s\r\n", results->valuestring);
        }
    }
    if (cjson) cJSON_Delete(cjson);
    return ret;
}

static void cb_ev_submit(struct mg_connection *nc, int ev, void *ev_data) {
    struct http_message *hm = (struct http_message *) ev_data;
    struct StWorkData *pQuestData = (struct StWorkData*)nc->user_data;
    char *p = 0;

    int is_chunked = 0;
    struct mg_str *s;
    if (hm) {
        if ((s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL &&
            mg_vcasecmp(s, "chunked") == 0) {
            is_chunked = 1;
        }
    }

    switch (ev) {
    case MG_EV_CONNECT:
        if (*(int *)ev_data != 0) {
            /*  fprintf(stderr, "connect() failed: %s\n", strerror(*(int *)ev_data));*/
            pQuestData->status = -1;
        }
        if (pQuestData->new_data) {
            free(pQuestData->new_data);
        }
        pQuestData->new_data = 0;
        pQuestData->new_len = 0;
        break;
    case MG_EV_HTTP_REPLY:
    {
        switch (hm->resp_code) {
        case 200:
            nc->flags |= MG_F_CLOSE_IMMEDIATELY;

            if (hm->body.len <= 0) {
                if (pQuestData->status  == 0) {
                    pQuestData->status  = -1;
                }
                break;
            }
            //process the whole body.
            pQuestData->status = -1;
            p = realloc(pQuestData->new_data, hm->body.len+1);
            if (p == 0) {
              printf("error: cb_ev_submit->realloc MG_EV_HTTP_REPLY, p==null!\r\n");
              break;
            }
            pQuestData->new_data = p;
            pQuestData->new_len = hm->body.len+1;
            memcpy(pQuestData->new_data, hm->body.p, hm->body.len);
            pQuestData->new_data[hm->body.len] = 0;
            //process the whole body.
            if (GetSubmitResult(pQuestData->new_data, pQuestData->new_len) > 0) {
                pQuestData->status = 1;
            }
            else {
                pQuestData->status = -1;
            }
            break;
        case 302:
            pQuestData->status = -302;
            break;
        case 401:
            pQuestData->status = -401;
            break;
        default:
            pQuestData->status = -1;
            break;
        }
        break;
    }

    case MG_EV_HTTP_CHUNK:
    {
        if (!is_chunked) break;

        nc->flags = MG_F_DELETE_CHUNK;

        //hex_dump("cb_http_upload_handler MG_EV_HTTP_CHUNK:", hm->body.p, hm->body.len);
        if (hm->body.len > 0) {
            p = realloc(pQuestData->new_data, pQuestData->new_len + hm->body.len);
            if (p == 0) {
                printf("error: cb_ev_submit->realloc MG_EV_HTTP_CHUNK, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            memcpy(&pQuestData->new_data[pQuestData->new_len], hm->body.p, hm->body.len);
            pQuestData->new_len += hm->body.len;
        }
        else if (hm->body.len == 0) {//end flag
            if (pQuestData->status != 0) break;
            p = realloc(pQuestData->new_data, pQuestData->new_len+1);
            if (p == 0) {
                printf("error: cb_ev_submit->realloc MG_EV_HTTP_CHUNK, p==null!\r\n");
                break;
            }
            pQuestData->new_data = p;
            pQuestData->new_data[pQuestData->new_len] = 0;
            pQuestData->new_len += 1;

            //process the whole body.
            if (GetSubmitResult(pQuestData->new_data, pQuestData->new_len) > 0) {
                pQuestData->status = 1;
            }
            else {
                pQuestData->status = -1;
            }
        }
        break;
    }

    case MG_EV_CLOSE:
        break;
    default:
        break;
    }
}

int SubmitCommon(char *path, char *content)
{
    struct StWorkData user_data;
    memset(&user_data, 0, sizeof(user_data));
    struct StDevMgr *pDevMgr = &gDevMgr;
    int ret = 0;
    char *meta = 0;
    char *url = 0;
    struct mg_connection *nc;
    struct mg_mgr mgr;

    if (!is_wifi_connected()) return -1;

    printf("ENTRY: %s!\r\n", __FUNCTION__);

    if (pDevMgr->token_tmp[0] == 0) {
        printf("warning: 1token error!\r\n");
        return -401;
    }

    mg_mgr_init(&mgr, NULL);

    meta = (char*)malloc(1024);
    if (meta == 0) {
        ret = -1;
        goto END_SUBMIT;
    }

    url = (char*)malloc(1024);
    if (url == 0)  {
        ret = -1;
        goto END_SUBMIT;
    }

    user_data.new_len = 0;
    user_data.new_data = 0;

    sprintf(meta,
        "Accept: */*\r\n"
        "Connection: keep-alive\r\n"
        "User-Agent: Mongoose/6.15\r\n"
        "Cache-Control: no-cache\r\n"
        "Authorization: %s\r\n"
        "Content-Type: application/json\r\n", pDevMgr->token_tmp);
    sprintf(url, "http://%s%s", pDevMgr->host, path);
    nc = mg_connect_http(&mgr, cb_ev_submit, url, meta, content);
    if (nc == 0) goto END_SUBMIT;
    nc->user_data = &user_data;
    unsigned int timemark = DjyGetSysTime()/1000;
    unsigned int timeout_ms = 10000;
    while (user_data.status == 0) {
        if (DjyGetSysTime()/1000-timemark > timeout_ms) {
            printf("SubmitHWork: timeout break!\r\n");
            break;
        }
        mg_mgr_poll(&mgr, 500);
        //Djy_EventDelay(10*1000);
    }
    ret = user_data.status;
END_SUBMIT:
    mg_mgr_free(&mgr);
    if (meta) free(meta);
    if (url) free(url);
    if (user_data.new_data) {
         free(user_data.new_data);
         user_data.new_data = 0;
         user_data.new_len = 0;
     }
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
int SubmitForm(char *path, char *content, char *boundary)
{
    struct StWorkData user_data;
    memset(&user_data, 0, sizeof(user_data));
    struct StDevMgr *pDevMgr = &gDevMgr;
    int ret = 0;
    char *meta = 0;
    char *url = 0;
    struct mg_connection *nc;
    struct mg_mgr mgr;

    if (!is_wifi_connected()) return -1;

    printf("ENTRY: %s!\r\n", __FUNCTION__);

    if (pDevMgr->token_tmp[0] == 0) {
        printf("warning: 1token error!\r\n");
        return -401;
    }

    mg_mgr_init(&mgr, NULL);

    meta = (char*)malloc(1024);
    if (meta == 0) {
        ret = -1;
        goto END_SUBMIT;
    }

    url = (char*)malloc(1024);
    if (url == 0) {
        ret = -1;
        goto END_SUBMIT;
    }

    user_data.new_len = 0;
    user_data.new_data = 0;

    sprintf(meta,
        "Accept: */*\r\n"
        "Connection: keep-alive\r\n"
        "User-Agent: Mongoose/6.15\r\n"
        "Cache-Control: no-cache\r\n"
        "Authorization: %s\r\n"
        "Content-Type: multipart/form-data; boundary=%s\r\n", pDevMgr->token_tmp, boundary);

    sprintf(url, "http://%s%s", pDevMgr->host, path);
    nc = mg_connect_http(&mgr, cb_ev_submit, url, meta, content);
    if (nc == 0) goto END_SUBMIT;
    nc->user_data = &user_data;
    unsigned int timemark = DjyGetSysTime() / 1000;
    unsigned int timeout_ms = 5000;
    while (user_data.status == 0) {
        if (DjyGetSysTime()/1000-timemark > timeout_ms) {
            printf("SubmitHWork: timeout break!\r\n");
            break;
        }
        mg_mgr_poll(&mgr, 500);
        Djy_EventDelay(10 * 1000);
    }
    ret = user_data.status;
END_SUBMIT:
    mg_mgr_free(&mgr);
    if (meta) free(meta);
    if (url) free(url);
    if (user_data.new_data) {
        free(user_data.new_data);
        user_data.new_data = 0;
        user_data.new_len = 0;
    }
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}


int user_login()
{
    int ret = 0;
    struct StDevMgr *pDevMgr = &gDevMgr;


    if (!is_wifi_connected()) return -1;

    printf("ENTRY: %s!\r\n", __FUNCTION__);

    unsigned int timestamp = 0;
    ret = GetTimeStamp(&timestamp, 5000);
    if (timestamp == 0) {
        printf("error: user_login->GetTimeStamp, failed!\r\n");
        return -1;
    }

    char *sn = (char*)pDevMgr->sn;
    char timestamp_str[50] = "";
    sprintf(timestamp_str, "%d000", timestamp);
    char key[100] = "";
    strcat(key, pDevMgr->key);
    strcat(key, timestamp_str);

    unsigned char out[20] = "";
    memset(out, 0, sizeof(out));
    cs_hmac_sha1((unsigned char*)key, strlen(key), (unsigned char*)sn, strlen(sn), out);
    char sign[100] = { 0 };
    memset(sign, 0, sizeof(sign));
    cs_base64_encode(out, sizeof(out), sign);
    //printf("%s", sign);

    pDevMgr->token_tmp[0] = 0;
    ret = http_login(sn, sign, timestamp_str, "---011000010111000001101001");
    if (pDevMgr->token_tmp[0] == 0) {
        //clear timestamp and renew from network
        //ClearTimeStamp();
        ret = -1;
    }
    printf("EXIT: %s!\r\n", __FUNCTION__);

    return ret;
}


int DevGetCommon(char *path, char *out_json, int len)
{
    struct StWorkData user_data;
    memset(&user_data, 0, sizeof(user_data));
    struct StDevMgr *pDevMgr = &gDevMgr;
    int ret = 0;
    char *meta = 0;
    char *url = 0;
    struct mg_connection *nc;
    struct mg_mgr mgr;

    if (pDevMgr->token_tmp[0] == 0) {
        printf("warning: token error!\r\n");
        return -401;
    }

    mg_mgr_init(&mgr, NULL);

    meta = (char*)malloc(1024);
    if (meta == 0) return -1;

    url = (char*)malloc(1024);
    if (url == 0) return -1;

    user_data.new_len = 0;
    user_data.new_data = 0;

    sprintf(meta,
        "Accept: */*\r\n"
        "Connection: keep-alive\r\n"
        "User-Agent: Mongoose/6.15\r\n"
        "Cache-Control: no-cache\r\n"
        "Authorization: %s\r\n"
        "Content-Type: application/json\r\n", pDevMgr->token_tmp);

    sprintf(url, "http://%s%s", pDevMgr->host, path);
    nc = mg_connect_http(&mgr, cb_ev_handler, url, meta, 0);
    if (nc == 0) goto END_QUEST;
    nc->user_data = &user_data;
    unsigned int timemark = DjyGetSysTime()/1000;
    unsigned int timeout_ms = 30000;
    while (user_data.status == 0) {
        if (DjyGetSysTime()/1000-timemark > timeout_ms) {
            printf("DevGetCommon: timeout break!\r\n");
            break;
        }
        mg_mgr_poll(&mgr, 500);
        Djy_EventDelay(10*1000);
    }
    if (user_data.status == 1) {
        if (user_data.new_data) {
            int min = strlen(user_data.new_data) + 1;
            min = min < len ? min : len;
            memcpy(out_json, user_data.new_data, min);
            user_data.status = min;
        }
        else {
            user_data.status = 0;
        }
    }
    ret = user_data.status;
END_QUEST:
    mg_mgr_free(&mgr);
    if (meta) free(meta);
    if (url) free(url);
    if (user_data.new_data) {
        free(user_data.new_data);
        user_data.new_data = 0;
        user_data.new_len = 0;
    }
    return ret;
}

int DevDeleteCommon(char *path)
{
    struct StWorkData user_data;
    memset(&user_data, 0, sizeof(user_data));
    struct StDevMgr *pDevMgr = &gDevMgr;
    int ret = 0;
    char *temp = 0;
    struct mg_connection *nc;
    struct mg_mgr mgr;

    if (pDevMgr->token_tmp[0] == 0) {
        printf("warning: token error!\r\n");
        return -401;
    }

    mg_mgr_init(&mgr, NULL);

    temp = (char*)malloc(1024);
    if (temp == 0) goto END_DELETE;

    user_data.new_len = 0;
    user_data.new_data = 0;


    memset(temp, 0, sizeof(temp));
    sprintf(temp, "%s:%d", pDevMgr->host, 80);
    nc = mg_connect(&mgr, temp, cb_ev_submit);

    if (nc == 0) goto END_DELETE;
    nc->user_data = &user_data;
    mg_set_protocol_http_websocket(nc);

    memset(temp, 0, sizeof(temp));
    sprintf(temp,
        "DELETE %s HTTP/1.1\r\n"
        "Host: %s\r\n"
        "User-Agent: Mongoose/6.15\r\n"
        "Cache-Control: no-cache\r\n"
        "Authorization: %s\r\n\r\n", path, pDevMgr->host, pDevMgr->token_tmp);
    printf("%s", temp);
    mg_printf(nc, "%s", temp);

    unsigned int timemark = DjyGetSysTime()/1000;
    unsigned int timeout_ms = 5000;
    while (user_data.status == 0) {
        if (DjyGetSysTime()/1000-timemark > timeout_ms) {
            printf("DevDeleteCommon: timeout break!\r\n");
            break;
        }
        mg_mgr_poll(&mgr, 500);
        Djy_EventDelay(10 * 1000);
    }
    ret = user_data.status;

END_DELETE:
    mg_mgr_free(&mgr);
    if (temp) free(temp);
    if (user_data.new_data) {
        free(user_data.new_data);
        user_data.new_data = 0;
        user_data.new_len = 0;
    }
    return ret;
}
#ifndef ANALOG_DATA_DEBUG
int ClassList(char *out_json, int len)
{
    char path[100] = {0};
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    strcpy(path, "/class/list");
    int ret  = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int DevGetALLInfo(int news_start, int news_end, int phw_end, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/api?n_star=%d&n_end=%d&p_end=%d", news_start, news_end, phw_end);
    int ret = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}


int DevGetPastHWork(int id, char *out_json, int len)
{
    char path[100] = {0};
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/plist?id=%d", id);
    //sprintf(path, "/device/plist?id=%d&page=%d&size=%d", id, page_num, page_size);
    int ret  = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
int DevGetTodayHWork(int id, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/tlist?id=%d", id);
    int ret  =   DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
int DevGetTodayHWorkList(int cid, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/today?cid=%d", cid);
    int ret  =  DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
int DevGetPastHWorkList(int cid, int pos_start, int pos_end,  char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/past?cid=%d&start=%d&end=%d", cid, pos_start, pos_end);
    int ret  =  DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
int DevGetUnfinishedHWork(int qid, int hid, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/unfinished?qid=%d&hid=%d", qid, hid);
    int ret  =  DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int DevGetFinishedHWork(int qid, int hid, int uid, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/finished?qid=%d&hid=%d&uid=%d", qid, hid, uid);
    int ret  =  DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int DevGetTopicQuestion(int qid, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/device/question?qid=%d", qid);
    int ret = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
int DevGetNewsList(int pos_start, int pos_end, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/news/list?start=%d&end=%d", pos_start, pos_end);
    int ret = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int GetTopicList(int hid, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/work/questions?hid=%d", hid);
    int ret = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}


int SubmitHWork(char *homework_str)
{
    int ret = 0;
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    ret = SubmitCommon("/work/commit", homework_str);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int DevMarkQueDone(int hid, int qid, char *out_json, int len)
{
	char path[100] = { 0 };
	printf("ENTRY: %s!\r\n", __FUNCTION__);
	sprintf(path, "/device/correct?hid=%d&qid=%d", hid, qid);
	int ret = DevGetCommon(path, out_json, len);
	printf("EXIT: %s!\r\n", __FUNCTION__);
	return ret;
}


int DevGetHWorkInfo(int id, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/work/info?id=%d", id);
    int ret = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int DevGetHWorkDIYQuestion(int hid, char *out_json, int len)
{
    char path[100] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/homework/diy/question?hid=%d", hid);
    int ret = DevGetCommon(path, out_json, len);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}


int DeleteNews(char *news_ids)
{
    char path[200] = { 0 };
    printf("ENTRY: %s!\r\n", __FUNCTION__);
    if (!is_wifi_connected()) return -1;
    sprintf(path, "/news/del?id=%s", news_ids);
    int ret = DevDeleteCommon(path);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}

int MarkedNews(int id)
{
    char *boundary = "---011000010111000001101001";
    int ret = 0;
    printf("ENTRY: %s!\r\n", __FUNCTION__);

    const char multi_part_req_fmt[] =
        "--%s\r\n"
        "Content-Disposition: form-data; name=\"id\"\r\n"
        "\r\n"
        "%d"
        "\r\n--%s--\r\n"
        "\r\n";

    char *multi_part_req = malloc(2 * 1024);
    if (multi_part_req == 0) {
        ret = -1;
        goto END_MARKEDNEWS;
    }
    snprintf(multi_part_req, 2 * 1024, multi_part_req_fmt, boundary, id, boundary);
    //printf("%s!\r\n", multi_part_req);
    ret = SubmitForm("/news/read", multi_part_req, boundary);
END_MARKEDNEWS:
    if (multi_part_req) free(multi_part_req);
    printf("EXIT: %s!\r\n", __FUNCTION__);
    return ret;
}
#endif

int is_wifi_connected();
extern int login_ok;
void device_user_test()
{
    int ret = 0;
    char buf[2048] = { 0 };

    while (!is_wifi_connected() || login_ok==0) {
        Djy_EventDelay(1000*1000);
    }

    DevMgrInit();
    user_login();

    ret = DevGetALLInfo(0,1,1,buf, sizeof(buf));
    printf("DevGetALLInfo, ret=%d!\r\n%s\r\n", ret, buf);

    ret = DevGetHWorkInfo(2, buf, sizeof(buf));
    printf("DevGetHWorkInfo, ret=%d!\r\n%s\r\n", ret, buf);

    ret = GetTopicList(1, buf, sizeof(buf));
    printf("GetTopicList, ret=%d!\r\n%s\r\n", ret, buf);

    ret = ClassList(buf, sizeof(buf));
    printf("ClassList, ret=%d!\r\n%s\r\n", ret, buf);

    ret = DeleteNews("1,2,3,123");
    printf("DeleteNews, ret=%d!\r\n", ret);

    ret = MarkedNews(174);
    printf("MarkedNews, ret=%d!\r\n", ret);

    DevGetPastHWorkList(117, 1, 10, buf, sizeof(buf));
    printf("DevGetPastHWorkList, ret=%d!\r\n%s\r\n", ret, buf);

    DevGetPastHWork(99, buf, sizeof(buf));
    printf("DevGetPastHWork, ret=%d!\r\n%s\r\n", ret, buf);

    DevGetTodayHWorkList(118, buf, sizeof(buf));
    printf("DevGetTodayHWorkList, ret=%d!\r\n%s\r\n", ret, buf);

    DevGetTodayHWork(108, buf, sizeof(buf));
    printf("DevGetTodayHWork, ret=%d!\r\n%s\r\n", ret, buf);

#if 0
    DevGetPastHWork(1, buf, sizeof(buf));
    DevGetTodayHWork(1, buf, sizeof(buf));

    DevGetTodayHWorkList(1, buf, sizeof(buf));

    DevGetPastHWorkList(1, 1, 10, buf, sizeof(buf));

    DevGetUnfinishedHWork(1, 1, buf, sizeof(buf));

    DevGetFinishedHWork(1, 1, 1, buf, sizeof(buf));
#endif
#if 0
    struct StHWorkTopic arr_topic[3] = {
    { 1, "abc.wav"},
    { 2, "bbb.wav" },
    { 3, "ccc.wav" },
    };

    char output[2048] = { 0 };
    gen_json_homework(2, arr_topic, sizeof(arr_topic) / sizeof(arr_topic[0]), output, sizeof(output));
    char buf[2048] = { 0 };
    //ret = SubmitHWork(output);
    //printf("SubmitHWork, ret=%d!\r\n", ret);

    ret = 0;
    ret = ClassList(buf, sizeof(buf));
    printf("ClassList, ret=%d!\r\n%s\r\n", ret, buf);
#endif
}
